﻿using blog.Entity;
using blog.Model.Dto;
using blog.Model.Vo;
using Microsoft.EntityFrameworkCore;
using System.IdentityModel.Tokens.Jwt;
using System.Linq.Expressions;
using System.Security.Claims;
using System.Text;
using blog.Model.Oo;
using blog.Service.HelpFunc;
using Microsoft.IdentityModel.Tokens;
using Newtonsoft.Json;
using ZstdSharp.Unsafe;

namespace blog.Service
{
    public class UserService : IUserService
    {
        BlogContext ef;

        public UserService(BlogContext ctx)
        {
            ef = ctx;
        }
        
        public async Task<AjaxResult> Insert(SysUser param)
        {
            try
            {
                var f = ef.SysUsers.Where(x => x.username == param.username).Count();
                if (f > 0)
                {
                    return AjaxResult.Error("账号已存在");
                }

                ef.SysUsers.Add(param);
                int res = await ef.SaveChangesAsync();
                if (res == 0)
                {
                    return AjaxResult.Error("刷新试试");
                }
                else
                {
                    return AjaxResult.Success();
                }
            }
            catch (Exception ex)
            {
                return AjaxResult.Error(ex.Message);
            }
        }

        public async Task<AjaxResult> Select(ListModel param,SearchKey key)
        {
            int page = param.page;
            int limit = param.limit;
            int offset = (page - 1) * limit;
            dynamic li;
            Expression<Func<SysUser, bool>> expression = ExpressionExtension.True<SysUser>();
            expression = expression.And(x => x.is_delete == 0);

            if (!string.IsNullOrEmpty(key.username))
            {
                string username = key.username;
                expression = expression.And(x => x.username.Contains(username));
            }

            if (!string.IsNullOrEmpty(key.nickname))
            {
                string nickname = key.nickname;
                expression = expression.And(x => x.nickname.Contains(nickname));
            }
            
            if (limit  == -1)
            {
                li = await ef.SysUsers
                    .Where(expression)
                    .OrderBy(x=>x.id)
                    .Select(x=>new UserView
                    {
                        id = x.id,
                        username = x.username,
                        nickname = x.nickname,
                        group_id = x.group_id,
                        group_name = ef.SysGroups.Where(g=>g.id == x.group_id).FirstOrDefault().group_name,
                        email = x.email,
                        avatar = ef.UserAvatars.Where(u=>u.user_id == x.id).FirstOrDefault().avatar,
                        status = x.status
                    })
                    .ToListAsync();
            }
            else
            {
                li = await ef.SysUsers
                    .Where(expression)
                    .OrderBy(x=>x.id)
                    .Skip(offset)
                    .Take(limit)
                    .Select(x => new UserView
                    {
                        id = x.id,
                        username = x.username,
                        nickname = x.nickname,
                        group_id = x.group_id,
                        group_name = ef.SysGroups.Where(g => g.id == x.group_id).FirstOrDefault().group_name,
                        email = x.email,
                        avatar = ef.UserAvatars.Where(u => u.user_id == x.id).FirstOrDefault().avatar,
                        status = x.status
                    })
                    .ToListAsync();
            }
            int count = ef.SysUsers.Where(expression).Count();
            return AjaxResult.Success(li,count);
        }

        public async Task<AjaxResult> Update(SysUser param)
        {
            try
            {
                var user = await ef.SysUsers.FindAsync(param.id);
                if (user == null)
                {
                    return AjaxResult.Error("刷新试试");
                }
                user.nickname = param.nickname;
                user.email = param.email;
                user.group_id = param.group_id;
                int res = await ef.SaveChangesAsync();
                if (res == 0)
                {
                    return AjaxResult.Error("无修改");
                }
                return AjaxResult.Success();
            }
            catch (Exception ex)
            {
                return AjaxResult.Error(ex.Message);
            }
        }
        
        public async Task<AjaxResult> Delete(List<long> id)
        {
            try
            {
                var users = ef.SysUsers.Where(x => id.Contains(x.id)).ToList();
                users.ForEach((x) => {
                    if (x.id != 1)
                    {
                        x.is_delete = 1;
                    }
                });
                ef.SysUsers.UpdateRange(users);
                int res = await ef.SaveChangesAsync();
                if (res == 0)
                {
                    return AjaxResult.Error("刷新试试");
                }
                return AjaxResult.Success();
            }
            catch (Exception ex)
            {
                return AjaxResult.Error(ex.Message);
            }
        }
        
        public async Task<AjaxResult> Find(long id)
        {
            throw new NotImplementedException();
        }

        public async Task<AjaxResult> ChangePwd(SysUser user)
        {
            try
            {
                var f = ef.SysUsers.Find(user.id);
                if (f != null)
                {
                    f.password = user.password;
                    int res = await ef.SaveChangesAsync();
                    if(res == 1)
                    {
                        return AjaxResult.Success();
                    }
                    else
                    {
                        return AjaxResult.Error("无修改");
                    }
                }
                else
                {
                    return AjaxResult.Error("出错啦，刷新试试");
                }
            }
            catch (Exception ex)
            {
                return AjaxResult.Error(ex.Message);
            }
        }

        public async Task<AjaxResult> ChangeStatus(SysUser user)
        {
            try
            {
                int res = await ef.SysUsers.Where(x => x.id == user.id)
                    .ExecuteUpdateAsync(x=>x.SetProperty(
                            entity=>entity.status,user.status
                        ));
                if (res == 0)
                {
                    return AjaxResult.Error("无修改");
                }
                return AjaxResult.Success();
            }
            catch (Exception ex)
            {
                return AjaxResult.Error(ex.Message);
            }
        }

        public async Task<AjaxResult> Validate(LoginModel param,string ip = "")
        {
            try
            {
                var f = ef.SysUsers.Where(x => x.username == param.username).FirstOrDefault();
                if (f == null)
                {
                    return AjaxResult.Error("账号不存在");
                }
                if (param.password != f.password)
                {
                    return AjaxResult.Error("密码错误");
                }
                if (f.status == 0)
                {
                    return AjaxResult.Error("该账号已禁用");
                }
                
                var claims = new[]
                {
                    new Claim(ClaimTypes.Name, f.username),
                    new Claim("userid",f.id.ToString()) 
                };
                var jwtOpt = param.jwtOpt;

                var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtOpt.SecurityKey));
                var signMethod = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
                var tokenOpt = new JwtSecurityToken(
                    issuer: jwtOpt.Issuer,
                    audience: jwtOpt.Audience,
                    claims: claims,
                    expires: DateTime.Now.AddDays(7),
                    signingCredentials: signMethod
                );
                var tokenStr = new JwtSecurityTokenHandler().WriteToken(tokenOpt);
                var token = new
                {
                    token = tokenStr
                };

                var getLastLog = ef.UserLogs.Where(x => x.user_id == f.id)
                    .OrderByDescending(x => x.log_time).FirstOrDefault();
                UserLog log = new UserLog()
                {
                    ip = ip,
                    user_id = f.id,
                    log_time = DateTime.Now
                };
                if (getLastLog == null || getLastLog.ip != ip)
                {
                    string getIpUrl = $"https://api.map.baidu.com/location/ip?ip={ip}&coor=bd09ll&ak=HUWNwlsBGdZk85kkDkfl3MCYVEqaTey1";
                    string res =  CurlHelper.GetData(getIpUrl);
                    var data = JsonConvert.DeserializeObject<IpApiRes>(res);
                    if (data != null)
                    {
                        if (data.status == "0")
                        {
                            log.location = data.content.address;
                        }
                        else
                        {
                            log.location = "未知地区或者海外";
                        }
                    }
                }
                else
                {
                    log.location = getLastLog.location;
                }
                await ef.UserLogs.AddAsync(log);
                await ef.SaveChangesAsync();
                return AjaxResult.Success(token);
            }
            catch (Exception ex)
            {
                return AjaxResult.Error(ex.Message);
            }
        }

        public async Task<AjaxResult> ListLog(ListModel param, SearchKey key)
        {
            try
            {
                int page = param.page;
                int limit = param.limit;
                int offset = (page - 1) * limit;
                Expression<Func<LogView, bool>> expression = ExpressionExtension.True<LogView>();
                
                var iq = from a in ef.UserLogs
                    join b in ef.SysUsers on a.user_id equals b.id into t1
                    from b in t1.DefaultIfEmpty()
                    join c in ef.SysGroups on b.group_id equals c.id into t2
                    from c in t2.DefaultIfEmpty()
                    select new LogView()
                    {
                        id = a.id,
                        user_id = a.user_id,
                        ip = a.ip,
                        location = a.location,
                        log_time = a.log_time,
                        username = b.username,
                        nickname = b.nickname,
                        group_name = c.group_name,
                        group_id = b.group_id
                    };
                int count = await iq.Where(expression).CountAsync();

                if (!string.IsNullOrEmpty(key.username))
                {
                    string username = key.username;
                    expression = expression.And(x => x.username.Contains(username));
                }

                if (!string.IsNullOrEmpty(key.nickname))
                {
                    string nickname = key.nickname;
                    expression = expression.And(x => x.nickname.Contains(nickname));
                }

                if (key.user_id != null)
                {
                    long user_id = key.user_id.Value;
                    expression = expression.And(x => x.user_id == user_id);
                }

                dynamic li;
                if (limit == -1)
                {
                    li = await iq.Where(expression)
                        .OrderByDescending(x=>x.id)
                        .ToListAsync();
                }
                else
                {
                    li = await iq.Where(expression)
                        .OrderByDescending(x=>x.log_time)
                        .Skip(offset)
                        .Take(limit)
                        .ToListAsync();
                }
                return AjaxResult.Success(li,count);
            }
            catch (Exception ex)
            {
                return AjaxResult.Error(ex.Message);
            }
        }


        

        

        
    }
}
